package com.ljb.lib_hiknvr.nvr.controller;

import android.os.Environment;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.view.SurfaceView;

import com.hikvision.netsdk.HCNetSDK;
import com.hikvision.netsdk.INT_PTR;
import com.hikvision.netsdk.NET_DVR_DEVICEINFO_V30;
import com.hikvision.netsdk.NET_DVR_DIGITAL_CHANNEL_STATE;
import com.hikvision.netsdk.NET_DVR_JPEGPARA;
import com.hikvision.netsdk.NET_DVR_PREVIEWINFO;
import com.ljb.lib_hiknvr.hcnetsdk.jna.HCNetSDKJNAInstance;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;

/**
 * 海康威视网络设备控制器,主要针对海康IVMS-4500客户端
 * 主要功能包括:
 * 初始化SDK,释放SDK;
 * 注册,注销;
 * 开始预览,停止预览;
 * 获取数字通道状态;
 * 云台控制,上,下,左,右
 * <p>
 * 所有涉及到网络请求的方法,需要在子线程运行
 * <p>
 * <p>
 * Created by ljb on 2018/8/14.
 */
public class HikNVRController {
    private static final String TAG = "HikHelper";
    private static HikNVRController controller;

    /**
     * 获取海康网络设备控制器
     *
     * @return 网络设备控制器
     */
    public static synchronized HikNVRController getInstance() {
        if (controller == null) {
            controller = new HikNVRController();
        }
        return controller;
    }

    /**
     * 初始化SDK
     *
     * @return 初始化SDK结果
     */
    public boolean initSDK() {
        if (!HCNetSDK.getInstance().NET_DVR_Init()) {
            log("initSDK: SDK初始化失败" + getLastErrorInfo());
            return false;
        }
        log("initSDK: SDK初始化成功");
        return true;
    }

    /**
     * 释放SDK资源
     *
     * @return 释放资源结果
     */
    public boolean cleanSDK() {
        if (!HCNetSDK.getInstance().NET_DVR_Cleanup()) {
            log("cleanSDK: SDK释放失败" + getLastErrorInfo());
            return false;
        }
        log("cleanSDK: SDK释放成功");
        return true;
    }

    /**
     * 登录网络设备
     *
     * @param ipAddr ip地址
     * @param port   端口号
     * @param user   用户名
     * @param pwd    密码
     * @return 登录成功后返回的数组, 数据分别是,[ 用户id, 开始通道号, 总共通道数]
     */
    public int[] login(String ipAddr, int port, String user, String pwd) {
        NET_DVR_DEVICEINFO_V30 deviceinfo_v30 = new NET_DVR_DEVICEINFO_V30();
        int userID = HCNetSDK.getInstance().NET_DVR_Login_V30(ipAddr, port, user, pwd, deviceinfo_v30);
        if (userID < 0) {
            log("login:登录失败 " + getLastErrorInfo());
            return new int[]{-1, -1, -1};
        }
        int startChan = 0;
        int chanNum = 0;
        if (deviceinfo_v30.byChanNum > 0) {//模拟通道,直连摄像头
            startChan = deviceinfo_v30.byStartChan;
            chanNum = deviceinfo_v30.byChanNum;
            log("login: 当前为模拟通道,直连摄像头");
        } else if (deviceinfo_v30.byIPChanNum > 0) {//数字通道,直连硬盘录像机
            startChan = deviceinfo_v30.byStartDChan;
            chanNum = deviceinfo_v30.byIPChanNum;
            log("login: 当前为数字通道,直连硬盘录像机");
        }
        log("userID = " + userID + ",开始通道号 = " + startChan + ",总共通道数 = " + chanNum);
        return new int[]{userID, startChan, chanNum};
    }

    /**
     * 获取数字通道状态
     *
     * @param userID 用户id
     * @return 返回所有数字通道状态的数组, 1表示可用, 其他表示不可用, 具体状态码参考海康网络设备文档
     */
    public int[] getChanState(int userID) {
        NET_DVR_DIGITAL_CHANNEL_STATE struChanState = new NET_DVR_DIGITAL_CHANNEL_STATE();
        boolean ret = HCNetSDK.getInstance().NET_DVR_GetDVRConfig(userID, HCNetSDK.NET_DVR_GET_DIGITAL_CHANNEL_STATE, -1, struChanState);
        if (!ret) {
            log("getDVRConfig: 获取数字通道状态失败 " + getLastErrorInfo());
        } else {
            log("getDVRConfig: 获取数字通道状态成功");
        }
        byte[] byDigitalChanState = struChanState.byDigitalChanState;
        int[] chanState = new int[byDigitalChanState.length];
        for (int i = 0; i < byDigitalChanState.length; i++) {
            chanState[i] = byDigitalChanState[i];
        }
        log("数字通道状态: " + Arrays.toString(chanState));
        return chanState;
    }

    /**
     * 用户注销
     *
     * @param userID 用户id
     * @return 注销结果, 成功返回true, 失败返回false
     */
    public boolean logout(int userID) {
        boolean ret = HCNetSDK.getInstance().NET_DVR_Logout_V30(userID);
        if (!ret) {
            log("logout: 用户注销失败 " + getLastErrorInfo());
            return false;
        }
        log("logout: 用户注销成功");
        return true;
    }

    /**
     * 开启预览
     *
     * @param userID      用户id
     * @param channel     通道号
     * @param streamType  码流类型
     * @param surfaceView 播放的view控件
     * @return 预览句柄,-1表示失败
     */
    public int startPlay(int userID, int channel, int streamType, SurfaceView surfaceView) {
        //创建预览信息对象
        NET_DVR_PREVIEWINFO previewinfo = new NET_DVR_PREVIEWINFO();
        //通道号
        previewinfo.lChannel = channel;
        //码流类型,0-主码流,1-子码流
        previewinfo.dwStreamType = streamType;
        //0-非阻塞取流,1阻塞取流
        previewinfo.bBlocked = 0;
        previewinfo.hHwnd = surfaceView.getHolder();
        //获取句柄
        return HCNetSDK.getInstance().NET_DVR_RealPlay_V40(userID, previewinfo, null);
    }

    /**
     * 停止预览
     *
     * @param realHandle 预览句柄
     * @return 成功返回true, 失败返回false
     */
    public boolean stopPlay(int realHandle) {
        return HCNetSDK.getInstance().NET_DVR_StopRealPlay(realHandle);
    }


    /**
     * 设备抓图
     *
     * @param userID  用户id
     * @param channel 通道号
     * @param path    保存路径
     * @return 成功返回true, 失败返回false
     */
    public boolean capture(int userID, int channel, String path) {
        if (TextUtils.isEmpty(path)) {
            path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/0Capture/";
        } else {
            path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + path + File.separator;
        }
        File file = new File(path);
        if (!file.exists() && !file.mkdirs()) {
            log("capture: 设备抓图失败,无效路径");
            return false;
        }
        //生成图片名称
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault());
        String date = format.format(new Date());
        path += date + ".jpg";
        NET_DVR_JPEGPARA lpJpegPara = new NET_DVR_JPEGPARA();
        //使用当前码流分辨率
        lpJpegPara.wPicSize = 0xff;
        //图片质量设置为最好
        lpJpegPara.wPicQuality = 0;
        boolean ret = HCNetSDK.getInstance().NET_DVR_CaptureJPEGPicture(userID, channel, lpJpegPara, path);
        if (!ret) {
            log("capture: 设备抓图失败 " + getLastErrorInfo());
            return false;
        }
        log("capture: 设备抓图成功 path = " + path);
        return true;
    }

    /**
     * 开始捕获预览视频
     *
     * @param realHandle 预览句柄
     * @param path       保存路径
     * @return 成功返回true, 失败返回false
     */
    public boolean startRecord(int realHandle, String path) {
        if (TextUtils.isEmpty(path)) {
            path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/0Record/";
        } else {
            path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + path + File.separator;
        }
        File file = new File(path);
        if (!file.exists() && !file.mkdirs()) {
            log("capture: 捕获预览视频失败,无效路径");
            return false;
        }
        //生成图片名称
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault());
        String date = format.format(new Date());
        path += date + ".mp4";
        boolean ret = HCNetSDKJNAInstance.getInstance().NET_DVR_SaveRealData_V30(realHandle, 0x2, path);
        if (!ret) {
            log("startRecord: 预览视频捕获失败" + getLastErrorInfo());
            return false;
        }
        log("startRecord: 预览视频捕获成功,储存路径为: " + path);
        return true;
    }

    /**
     * 停止捕获预览视频
     *
     * @param realHandle 当前预览句柄
     * @return 成功返回true, 失败返回false
     */
    public boolean stopRecord(int realHandle) {
        boolean ret = HCNetSDK.getInstance().NET_DVR_StopSaveRealData(realHandle);
        if (!ret) {
            log("stopRecord: 预览视频捕获停止失败" + getLastErrorInfo());
            return false;
        }
        log("stopRecord: 预览视频捕获停止成功");
        return true;
    }

    /**
     * 云台控制
     *
     * @param userID     用户id
     * @param channel    通道号
     * @param ptzCommand 云台控制命令,包括上下左右移动,及焦距缩放等,具体命令参考{@link PTZCommand}接口中的参数
     * @param stop       云台控制行为,开始/停止
     * @return 命令控制结果, true表示成功, false表示失败
     */
    public boolean ptzControl(int userID, int channel, int ptzCommand, int stop) {
        boolean ret = HCNetSDK.getInstance().NET_DVR_PTZControl_Other(userID, channel, ptzCommand, stop);
        if (!ret) {
            log("ptzControl: 云台命令控制失败 " + getLastErrorInfo());
            return false;
        }
        log("ptzControl: 云台命令控制成功 ptzCommand=" + ptzCommand);
        return true;
    }

    /**
     * 云台控制
     *
     * @param userID     用户id
     * @param channel    通道号
     * @param ptzCommand 云台控制命令,包括上下左右移动,及焦距缩放等,具体命令参考{@link PTZCommand}接口中的参数
     * @param duration   命令执行持续时长,单位ms
     * @return 命令控制结果, true表示成功, false表示失败
     */
    public boolean ptzControl(int userID, int channel, int ptzCommand, long duration) {
        boolean ret = ptzControl(userID, channel, ptzCommand, 0);
        if (!ret) {
            return false;
        }
        SystemClock.sleep(duration);
        return ptzControl(userID, channel, ptzCommand, 1);
    }

    /**
     * 云台控制(默认命令执行持续时间为200ms)
     *
     * @param userID     用户id
     * @param channel    通道号
     * @param ptzCommand 云台控制命令,包括上下左右移动,及焦距缩放等,具体命令参考{@link PTZCommand}接口中的参数
     * @return 命令控制结果, true表示成功, false表示失败
     */
    public boolean ptzControl(int userID, int channel, int ptzCommand) {
        return ptzControl(userID, channel, ptzCommand, 200L);
    }


    /**
     * 云台上仰
     *
     * @param userID  用户id
     * @param channel 通道号
     * @return 命令控制结果, true表示成功, false表示失败
     */
    public boolean ptzUp(int userID, int channel) {
        return ptzControl(userID, channel, PTZCommand.TILT_UP);
    }

    /**
     * 云台下俯
     *
     * @param userID  用户id
     * @param channel 通道号
     * @return 命令控制结果, true表示成功, false表示失败
     */
    public boolean ptzDown(int userID, int channel) {
        return ptzControl(userID, channel, PTZCommand.TILT_DOWN);
    }

    /**
     * 云台平行左转
     *
     * @param userID  用户id
     * @param channel 通道号
     * @return 命令控制结果, true表示成功, false表示失败
     */
    public boolean ptzLeft(int userID, int channel) {
        return ptzControl(userID, channel, PTZCommand.PAN_LEFT);
    }

    /**
     * 云台平行右转
     *
     * @param userID  用户id
     * @param channel 通道号
     * @return 命令控制结果, true表示成功, false表示失败
     */
    public boolean ptzRight(int userID, int channel) {
        return ptzControl(userID, channel, PTZCommand.PAN_RIGHT);
    }

    /**
     * 云台放大
     *
     * @param userID  用户id
     * @param channel 通道号
     * @return 命令控制结果, true表示成功, false表示失败
     */
    public boolean ptzZoomIn(int userID, int channel) {
        return ptzControl(userID, channel, PTZCommand.ZOOM_IN);
    }

    /**
     * 云台缩小
     *
     * @param userID  用户id
     * @param channel 通道号
     * @return 命令控制结果, true表示成功, false表示失败
     */
    public boolean ptzZoomOut(int userID, int channel) {
        return ptzControl(userID, channel, PTZCommand.ZOOM_OUT);
    }

    //**************************分割线**********************************

    //获取最后一条错误信息
    private String getLastErrorInfo() {
        int lastErrorCode = HCNetSDK.getInstance().NET_DVR_GetLastError();
        INT_PTR int_ptr = new INT_PTR();
        int_ptr.iValue = lastErrorCode;
        return "errorCode: " + lastErrorCode + " ;errorMessage: " + HCNetSDK.getInstance().NET_DVR_GetErrorMsg(int_ptr);
    }

    //输出日志
    public void log(String logInfo) {
        Log.e(TAG, logInfo);
    }
}
